package cz.boxdesign.library.web;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import cz.boxdesign.library.common.dao.DatabaseOperationException;
import cz.boxdesign.library.entity.Author;
import cz.boxdesign.library.entity.Book;
import cz.boxdesign.library.entity.Napsal;
import cz.boxdesign.library.service.AuthorService;
import cz.boxdesign.library.service.BookService;
import cz.boxdesign.library.service.DepartmentService;
import cz.boxdesign.library.service.PublisherService;
import cz.boxdesign.library.service.NapsalService;
import cz.boxdesign.library.web.validation.BookValidator;
import cz.boxdesign.library.web.view.BookView;

@Controller
@RequestMapping("/books")
public class BookController {

	protected final Log log = LogFactory.getLog(BookController.class);

	@Autowired
	private BookService knihaService;

	@Autowired
	private AuthorService autorService;

	@Autowired
	@Qualifier("departmentService")
	private DepartmentService departmentService;

	@Autowired
	private NapsalService napsalService;

	@Autowired
	@Qualifier("publisherService")
	private PublisherService publisherService;

	@InitBinder
	public void initModel(WebDataBinder binder) {
		Object targer = binder.getTarget();
		if (targer instanceof BookView) {
			if (binder.getValidator() == null) {
				binder.setValidator(new BookValidator());
			}
		}
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView showList(HttpServletRequest request) {
		ModelAndView model = new ModelAndView("books/showList");
		if (request.getSession().getAttribute("message") != null) {
			model.addObject("message", request.getSession().getAttribute("message"));
			request.getSession().removeAttribute("message");
		}
		try {
			model.addObject("bookView", new BookView());
			model.addObject("knihaList", knihaService.getAll());
		} catch (DatabaseOperationException e) {
			model.addObject("error", e.getMessage());
		}
		return model;
	}

	@RequestMapping("/find")
	public ModelAndView find(HttpServletRequest request, BookView view) {
		ModelAndView model = new ModelAndView("books/showList");
		try {
			if (view.getNazev() != null || view.getNazev() != "") {
				model.addObject("bookView", new BookView());
				model.addObject("knihaList", knihaService.getByClause("and nazev_k like '%" + view.getNazev() + "%'"));
			}else{
				model.addObject("message","Field Name Of Book is required !");
			}
		} catch (DatabaseOperationException e) {
			model.addObject("error", e.getMessage());
		}
		return model;
	}

	@RequestMapping(value = "/show", method = RequestMethod.GET)
	public ModelAndView showBook(HttpServletRequest request) {
		ModelAndView model = new ModelAndView("books/show");
		try {
			BookView book = new BookView();
			prepareView(request, book);
			Integer bookId = Integer.valueOf(request.getParameter("bookId"));
			List<Napsal> napsalList = napsalService.getByClause("and kniha_id_k = " + bookId);
			List<Author> autorList = new ArrayList<Author>();
			for (Napsal napsal : napsalList) {
				autorList.add(napsal.getAutor());
			}
			book.setAutorList(autorList);
			model.addObject("book", book);
		} catch (DatabaseOperationException e) {
			model.addObject("error", e.getMessage());
		}
		return model;
	}

	@RequestMapping(value = "/new", method = RequestMethod.GET)
	public ModelAndView newBook(HttpServletRequest request) throws IllegalAccessException, InvocationTargetException {
		ModelAndView model = request.getSession().getAttribute("model") != null ? (ModelAndView) request.getSession().getAttribute("model")
				: new ModelAndView();
		insertOrUpdate(request, model);
		return model;
	}

	@RequestMapping("/edit")
	public ModelAndView editBook(HttpServletRequest request) throws IllegalAccessException, InvocationTargetException {
		ModelAndView model = new ModelAndView();
		insertOrUpdate(request, model);
		return model;
	}

	/**
	 * @param request
	 * @param model
	 */
	private void insertOrUpdate(HttpServletRequest request, ModelAndView model) {
		BookView book = new BookView();
		try {
			book.setAutorList(autorService.getAll());
			book.setNakladatelstviList(publisherService.getAll());
			book.setOddeleniList(departmentService.getAll());
			if (request.getParameter("bookId") != null) {
				Integer bookId = Integer.valueOf(request.getParameter("bookId"));
				prepareView(request, book);
				model.addObject("action", "/books/submit.do?bookId=" + bookId);
				model.setViewName("books/edit");
			} else {
				model.addObject("action", "/books/submit.do");

				model.setViewName("books/new");
			}
		} catch (DatabaseOperationException e) {
			model.addObject("error", e.getMessage());
		}
		model.addObject("book", book);
	}

	/**
	 * @param request
	 * @param book
	 */
	private void prepareView(HttpServletRequest request, BookView book) {
		Integer bookId = Integer.valueOf(request.getParameter("bookId"));
		Book k = knihaService.getById(bookId);
		book.setId(k.getId());
		List<Napsal> napsalList = napsalService.getByClause("and kniha_id_k = " + k.getId());
		List<Integer> autorIds = new ArrayList<Integer>();
		for (Napsal napsal : napsalList) {
			autorIds.add(napsal.getAutor().getId());
		}
		book.setAutorIds(autorIds);
		book.setNakladatelstviId(k.getNakladatelstvi().getId());
		book.setNakladatelstvi(k.getNakladatelstvi());
		book.setOddeleni(k.getOddeleni());
		book.setOddeleniId(k.getOddeleni().getId());
		book.setNazev(k.getNazev());
		book.setRokVydani(k.getRokVydani());
		book.setOddeleniId(k.getOddeleni().getId());
		book.setPocetKnih(k.getPocetKnih());
	}

	private List<Integer> getListOfAuthorIds(List<Author> seznamAutoru) {
		List<Integer> ids = new ArrayList<Integer>();
		for (Author autor : seznamAutoru) {
			ids.add(autor.getId());
		}
		return ids;
	}

	@RequestMapping("/delete")
	public ModelAndView deleteBook(@RequestParam("bookId") Integer bookId) {
		Book book = knihaService.getById(bookId);
		List<Napsal> napsal = napsalService.getByClause("and kniha_ID_k = " + bookId);
		napsalService.batchDelete(napsal);
		knihaService.deleteEntity(book);
		return new ModelAndView(new RedirectView("../books.do"));
	}

	@RequestMapping("/deleteFromAuthor")
	public ModelAndView deleteBookFromAuthor(@RequestParam("bookId") Integer bookId, @RequestParam("authorId") Integer authorId) {
		Napsal n = napsalService.getByClause("and autor_id_a=" + authorId + " and kniha_id_k=" + bookId).get(0);
		napsalService.deleteEntity(n);
		return new ModelAndView(new RedirectView("/isLibrary/authors/show.do?authorId=" + authorId));
	}

	@RequestMapping(value = "/submit", method = RequestMethod.POST)
	public ModelAndView submitBook(@RequestParam("bookId") Integer bookId, BookView view, HttpServletRequest request, BindingResult error) {
		Book book = new Book();
		List<Error> errorList = validate(view);
		if (errorList.isEmpty() && !error.hasErrors()) {
			try {
				if (bookId != null) {
					book = knihaService.getById(bookId);
					copyFromView(view, book);
					List<Napsal> submited = createListOfWritings(view, book);
					List<Napsal> writes = napsalService.getByClause("and kniha_id_k = " + bookId);
					for (Napsal napsal : writes) {
						for (Napsal napsal2 : submited) {
							if (napsal.getAutor().getId().equals(napsal2.getAutor().getId())) {
								submited.remove(napsal2);
								break;
							}
						}
					}
					knihaService.updateEntity(book);
					napsalService.batchInsert(submited);
					return new ModelAndView(new RedirectView("show.do?bookId=" + book.getId()));
				} else {
					copyFromView(view, book);
					knihaService.insertEntity(book);
					book = knihaService.getByClause(
							"and nazev_k='" + book.getNazev() + "' and id_o=" + book.getOddeleni().getId() + " and id_n="
									+ book.getNakladatelstvi().getId() + " order by id").get(0);
					List<Napsal> submited = createListOfWritings(view, book);
					napsalService.batchInsert(submited);
					return new ModelAndView(new RedirectView("show.do?bookId=" + book.getId()));
				}
			} catch (DatabaseOperationException e) {
				log.error(e.getMessage(), e);
				request.getSession().setAttribute("error", e.getMessage());
			}
		}
		ModelAndView model = new ModelAndView();
		model.addObject("formErrors", errorList.isEmpty() ? null : errorList);
		model.addObject("book", view);
		if (bookId != null) {
			return new ModelAndView(new RedirectView("edit.do?bookId=" + bookId));
		} else {
			request.setAttribute("model", model);
			return new ModelAndView(new RedirectView("new.do"));
		}
	}

	private List<Error> validate(BookView view) {
		List<Error> errorList = new ArrayList<Error>();
		try {
			if (view.getRokVydani() != null) {
				Integer rok = Integer.valueOf(view.getRokVydani());
			} else {
				errorList.add(new Error("Year of Publication is required"));
			}
		} catch (NumberFormatException e) {
			errorList.add(new Error("Year of Publication has wrong format"));
		}

		try {
			if (view.getPocetKnih() != null) {
				Integer rok = Integer.valueOf(view.getPocetKnih());
			} else {
				errorList.add(new Error("Book Count is required"));
			}
		} catch (NumberFormatException e) {
			errorList.add(new Error("Book Count has wrong format"));
		}

		return errorList;
	}

	private List<Napsal> createListOfWritings(BookView view, Book book) {
		List<Napsal> list = new ArrayList<Napsal>();
		for (Integer authorId : view.getAutorIds()) {
			Napsal n = new Napsal();
			n.setAutor(autorService.getById(authorId));
			n.setKniha(book);
			list.add(n);
		}
		return list;
	}

	private void copyFromView(BookView view, Book book) {
		book.setNakladatelstvi(publisherService.getById(view.getNakladatelstviId()));
		book.setOddeleni(departmentService.getById(view.getOddeleniId()));
		book.setNazev(view.getNazev());
		book.setRokVydani(view.getRokVydani());
		book.setPocetKnih(view.getPocetKnih());
	}

}
